using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Autobot
{
    public enum MessageType : byte
    {
        NONE,
        SLEEP,               // This is a time to stall so we can create some time between action events
        
        ROBOT_POSITION,      // This is to set the robot position           Params: {float X} {float Y}             Usage: Client to Server
        ROBOT_DIRECTION,     // This is to update the robot's direction     Params: {float X} {float Y}             Usage: Client to Server
        ROBOT_VELOCITY,
        SENSOR_UPDATE,       // Update in the sensor readings               Params: {int WhichSensor} {float Value} Usage: Client to Server
        MOTOR_UPDATE,        // Update in the motor value                   Params: {int WhichMotor} {int value}    Usage: Client to Server
        MOTOR_SET,           // Command to set the motor to specific value  Params: {int WhichMotor} {int value}    Usage: Server to Client
        MOTOR_INC,           // Command to increment the motor setting      Params: {int WhichMotor} {int value}    Usage: Server to Client
        MOTOR_INVERT,        // Command to invert the current motor setting Params: {int WhichMotor}                Usage: Server to Client

        BURST_BEGIN,         // These two messages are to signal a batch of messages and if it is beginning or starting
        BURST_END,

        ROBOT_UPDATE,
        ROBOT_COMMAND,

        ALGORITHM_UPDATE,    // This message will be sent to the server to cause an algorithm update

        MAP_GET,             // get map status for an x,y position          Implement me later for network observer
        MAP_SET,             // set map status for an x,y position          Implement me later for network observer
        CLIENT_CONNECT,      // A client wishes to connect to the server    Params: {string IP}????
        CLIENT_DISCONNECT,   // A client is dying.
        NUM_MESSAGE_TYPES,
    }

    class Message
    {
        public const UInt32 magic_number = 6142;
        protected MessageType type = MessageType.NONE;
        protected UInt32 client_id = 0;

        private ArrayList field_list;
        
        public Message()
        {
            field_list = new ArrayList();
        }

        public MessageType GetMessageType()
        {
            return type;
        }

        public void SetMessageType(MessageType a_type)
        {
            type = a_type;
        }

        public UInt32 GetClientID()
        {
            return client_id;
        }

        public void SetClientID(UInt32 a_id)
        {
            client_id = a_id;
        }

        public string SaveToString()
        {
            string s;
            s = System.String.Format( "[0=6142][type={0}][client={1}]", (byte)type, client_id );
            int i;
            MessageField mf;
            for (i = 0; i < field_list.Count; i++)
            {
                mf = (MessageField)field_list[i];
                if (mf != null)
                {
                    s += System.String.Format("[{0}={1}]", mf.GetID(), mf.GetValue());
                }
            }
            return s;
        }

        public string GetField(string a_name)
        {
            int i;
            MessageField mf;
            for (i = 0; i < field_list.Count; i++)
            {
                mf = (MessageField)field_list[i];
                if (mf != null)
                {
                    if (mf.IsID(a_name))
                    {
                        return mf.GetValue();
                    }
                }
            }
            return null;
        }

        public void SetField(MessageField field)
        {
            int i;
            bool found = false;
            MessageField mf;
            for (i = 0; i < field_list.Count; i++)
            {
                mf = (MessageField)field_list[i];
                if (mf != null)
                {
                    if (mf.IsID(field.GetID()))
                    {
                        mf.SetValue(field.GetValue());
                        found = true;
                        break;
                    }
                }
            }

            if (!found)
            {
                mf = new MessageField(field.GetID());
                mf.SetValue(field.GetValue());
                field_list.Add(mf);
            }
        }
		
		// Helper functions
        public Message CreateNoneMsg(uint ClientID)
        {
            SetMessageType(MessageType.NONE);
            SetClientID(ClientID);
            return this;
        }
        public Message CreateBurstBeginMsg(uint ClientID, int NumMessages)
        {
            SetMessageType(MessageType.BURST_BEGIN);
            MessageField mf;
            mf = new MessageField("count", NumMessages.ToString());
            SetField(mf);
            SetClientID(ClientID);
            return this;
        }
        public Message CreateBurstEndMsg(uint ClientID)
        {
            SetMessageType(MessageType.BURST_END);
            SetClientID(ClientID);
            return this;
        }
        public Message CreateAlgorithmUpdateMsg(uint ClientID)
        {
            SetMessageType(MessageType.ALGORITHM_UPDATE);
            SetClientID(ClientID);
            return this;
        }
        public Message CreateUpdateMessage(uint ClientID, Vector2 Position, Vector2 Direction,float[] Sensors, int[] Motors, float Velocity)
        {
            SetMessageType(MessageType.ROBOT_UPDATE);
            MessageField mf;
            mf = new MessageField("PosX", Position.X.ToString());
            SetField(mf);
            mf = new MessageField("PosY", Position.Y.ToString());
            SetField(mf);
            mf = new MessageField("DirX", Direction.X.ToString());
            SetField(mf);
            mf = new MessageField("DirY", Direction.Y.ToString());
            SetField(mf);
            mf = new MessageField("Middle", Sensors[(int)SensorID.MIDDLE].ToString());
            SetField(mf);
            mf = new MessageField("Left", Sensors[(int)SensorID.LEFT].ToString());
            SetField(mf);
            mf = new MessageField("Right", Sensors[(int)SensorID.RIGHT].ToString());
            SetField(mf);
            mf = new MessageField("Drive", Motors[(int)MotorID.DRIVE].ToString());
            SetField(mf);
            mf = new MessageField("Steer", Motors[(int)MotorID.STEERING].ToString());
            SetField(mf);
            mf = new MessageField("Velocity", Velocity.ToString());
            SetField(mf);

            SetClientID(ClientID);
            return this;
        }
        public Message CreateSleepMsg(uint ClientID, int MS)
        {
            SetMessageType(MessageType.SLEEP);

            MessageField mf;
            mf = new MessageField("MS", MS.ToString());
            SetField(mf);
            SetClientID(ClientID);
            return this;
        }

        public Message CreateRobotPositionMsg(uint ClientID, Vector2 Position)
        {
            SetMessageType(MessageType.ROBOT_POSITION);

			MessageField mf;
            mf = new MessageField("x", Position.X.ToString());
            SetField(mf);
            mf = new MessageField("y", Position.Y.ToString());
            SetField(mf);
			
            SetClientID(ClientID);
            return this;
        }
        public Message CreateRobotDirectionMsg(uint ClientID, Vector2 Direction)
        {
            SetMessageType(MessageType.ROBOT_DIRECTION);

            MessageField mf;
            mf = new MessageField("x", Direction.X.ToString());
            SetField(mf);
            mf = new MessageField("y", Direction.Y.ToString());
            SetField(mf);

            return this;
        }
        public Message CreateRobotVelocityMsg(uint ClientID, float Velocity)
        {
            SetMessageType(MessageType.ROBOT_VELOCITY);

            MessageField mf;
            mf = new MessageField("velocity", Velocity.ToString());
            SetField(mf);

            SetClientID(ClientID);
            return this;
        }
        public Message CreateSensorUpdateMsg(uint ClientID, int WhichSensor, float Reading)
        {
            SetMessageType(MessageType.SENSOR_UPDATE);

            MessageField mf;
            mf = new MessageField("sensor_id", WhichSensor.ToString());
            SetField(mf);
            mf = new MessageField("reading", Reading.ToString());
            SetField(mf);

            SetClientID(ClientID);
            return this;
        }
        public Message CreateMotorUpdateMsg(uint ClientID, int WhichMotor, int Setting )
        {
            SetMessageType(MessageType.MOTOR_UPDATE);

            MessageField mf;
            mf = new MessageField("motor_id", WhichMotor.ToString());
            SetField(mf);
            mf = new MessageField("setting", Setting.ToString());
            SetField(mf);

            SetClientID(ClientID);
            return this;
        }
        public Message CreateMotorSetMsg(uint ClientID, int WhichMotor, int Setting, int DelayMS)
        {
            SetMessageType(MessageType.MOTOR_SET);

            MessageField mf;
            mf = new MessageField("motor_id", WhichMotor.ToString());
            SetField(mf);
            mf = new MessageField("setting", Setting.ToString());
            SetField(mf);
            mf = new MessageField("DelayMS", DelayMS.ToString());
            SetField(mf);

            SetClientID(ClientID);
            return this;
        }
        public Message CreateMotorIncMsg(uint ClientID, int WhichMotor, int HowMuch, int DelayMS)
        {
            SetMessageType(MessageType.MOTOR_INC);

            MessageField mf;
            mf = new MessageField("motor_id", WhichMotor.ToString());
            SetField(mf);
            mf = new MessageField("amount", HowMuch.ToString());
            SetField(mf);
            mf = new MessageField("DelayMS", DelayMS.ToString());
            SetField(mf);

            SetClientID(ClientID);
            return this;
        }
        public Message CreateMotorInvertMsg(uint ClientID, int WhichMotor, int DelayMS)
        {
            SetMessageType(MessageType.MOTOR_INVERT);

            MessageField mf;
            mf = new MessageField("motor_id", WhichMotor.ToString());
            SetField(mf);
            mf = new MessageField("DelayMS", DelayMS.ToString());
            SetField(mf);

            SetClientID(ClientID);
            return this;
        }
        public int GetNumParams()
        {
            return field_list.Count;
        }

    }

    class DelayedMessage
    {
        private Message message;
        private DateTime startTime;
        private int delayMS;

        public DelayedMessage(Message NewDelayedMessage, DateTime StartDelay, int DelayPeriodMS)
        {
            message = NewDelayedMessage;
            startTime = StartDelay;
            delayMS = DelayPeriodMS;
        }
        public Message GetMessage()
        {
            return message;
        }
        public DateTime GetStartTime()
        {
            return startTime;
        }
        public int GetDelayPeriod()
        {
            return delayMS;
        }
    }

}
